Skip to content

feat(retro): add flapping detection to retro analysis skill - #834

Open
Benkapner wants to merge 1 commit into
fullsend-ai:mainfrom
Benkapner:feat/retro-flapping-detection-v2
Open

feat(retro): add flapping detection to retro analysis skill#834
Benkapner wants to merge 1 commit into
fullsend-ai:mainfrom
Benkapner:feat/retro-flapping-detection-v2

Conversation

@Benkapner

Copy link
Copy Markdown

Summary

Adds a flapping detection section to the retro-analysis skill, teaching the retro agent to identify fix-break oscillation patterns during post-workflow analysis.

Supersedes #540, which accumulated 7 review rounds and diverged from the file's conventions. This version incorporates the key insight from that review process (the event_payload JSON in dispatch-repo logs exposes pull_request.head.sha and pull_request.number directly) and keeps the section at the same altitude as the rest of the file.

What it adds

Guidance for detecting three flapping patterns:

  • File oscillation: same file changed in consecutive runs with reversing diffs
  • Test result flipping: pass/fail/pass cycles correlated to agent-changed files
  • Cycle count: repeated review-fix cycles raising the same or alternating findings without convergence

Design decisions

  • Single high-level subagent prompt, matching the Workflow tracer / Trace reader / Comment analyzer pattern already in the file
  • Uses event_payload from dispatch-repo run logs for run-to-commit correlation (no timestamp approximation)
  • Cycle-count pattern covers both repeated and alternating findings (A then B then A)
  • "When NOT to flag" section prevents false positives on normal single-rework iterations

Related

Checklist

  • PR title follows Conventional Commits
  • Commits are signed off (DCO)

@Benkapner
Benkapner requested a review from a team as a code owner August 17, 2026 12:40
@github-actions

Copy link
Copy Markdown

Functional tests did not run

Functional tests run automatically for org/repo members and collaborators on pull requests.

For other contributors, a maintainer must add the ok-to-test label after the latest push.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add flapping detection guidance to retro-analysis skill

✨ Enhancement 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Add a dedicated flapping detection section to retro-analysis retro workflow.
• Define data collection via dispatch-repo event_payload for run↔commit correlation.
• Specify three oscillation patterns and guardrails to avoid false positives.
Diagram

graph TD
  RA["Retro analysis skill"] --> D{"PR-based workflow?"} -->|"Yes"| FC["Flapping data collector"] --> DL[("dispatch-repo logs")] --> GH["GitHub API / repo"] --> OUT["Retro proposal output"]
  D -->|"No"| SKIP["Skip detection"]

  subgraph Legend
    direction LR
    _doc["Process/step"] ~~~ _dec{"Decision"} ~~~ _db[("Data source")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Automate flapping metrics via a dedicated script/tool
  • ➕ More consistent detection (less prompt interpretation drift)
  • ➕ Easier to regression test and iterate on heuristics
  • ➖ Higher implementation/maintenance cost
  • ➖ Requires CI/log access plumbing beyond the skill text
2. Split flapping detection into its own skill file
  • ➕ Keeps SKILL.md shorter and more focused
  • ➕ Allows independent iteration/versioning of the flapping rubric
  • ➖ Adds navigation/indirection for the retro agent
  • ➖ May diverge from existing “single-file altitude” conventions in this area

Recommendation: Current approach is a good fit: it stays consistent with existing subagent-driven sections and uses event_payload for precise run↔commit correlation (avoiding timestamp heuristics). Consider automation only if flapping detection becomes frequent or needs high precision/benchmarking.

Files changed (1) +37 / -0

Documentation (1) +37 / -0
SKILL.mdAdd flapping detection section with patterns, data collection, and guardrails +37/-0

Add flapping detection section with patterns, data collection, and guardrails

• Introduces a new “Flapping detection” section that scopes applicability to PR-based workflows, defines a subagent data-collection prompt using dispatch-repo 'event_payload' correlation, and enumerates three oscillation patterns (file oscillation, test flipping, cycle count). Adds explicit guidance on what to include in a proposal when flapping is detected and when not to flag to reduce false positives.

skills/retro-analysis/SKILL.md

@qodo-code-review

qodo-code-review Bot commented Aug 17, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (1)

Grey Divider


Action required

1. Protected skills/ file modified 📜 Skill insight § Compliance
Description
This PR modifies skills/retro-analysis/SKILL.md, which is a protected governance/infrastructure
path requiring explicit human review and must not be auto-approved. Ensure appropriate
reviewers/CODEOWNERS sign off before merge.
Code

skills/retro-analysis/SKILL.md[R124-126]

+## Flapping detection
+
+Check whether the workflow exhibits fix-break oscillation. Flapping wastes agent cycles and often indicates a deeper problem (conflicting instructions, flaky tests, or an approach the agent cannot converge on).
Relevance

●● Moderate

Protected-path precedents support human review, but historical outcomes are undetermined rather than
accepted or rejected.

PR-#59
PR-#157

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The compliance checklist defines skills/ as a protected governance/infrastructure path; modifying
it requires raising a finding and ensuring human approval. The diff shows new content added under
skills/retro-analysis/SKILL.md.

skills/retro-analysis/SKILL.md[124-126]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR changes a protected path (`skills/`), which must not be auto-approved and requires explicit human review.

## Issue Context
Protected governance/infrastructure paths require elevated scrutiny. This PR updates the retro-analysis skill content under `skills/`.

## Fix Focus Areas
- skills/retro-analysis/SKILL.md[124-126]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Unresolved repo placeholders ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new flapping data collector prompt uses literal <DISPATCH_REPO> and <REPO> tokens even
though this skill defines $DISPATCH_REPO and uses $REPO_FULL_NAME in its recipes. If these
placeholders aren’t substituted, the subagent can run gh commands against an invalid/wrong repo
and the flapping analysis will fail or collect the wrong data.
Code

skills/retro-analysis/SKILL.md[136]

+- **Flapping data collector:** "Find all code, fix, and review workflow runs related to PR #<PR_NUMBER> in `<DISPATCH_REPO>`. Each run's log contains an `event_payload` JSON line with `pull_request.head.sha` and `pull_request.number`; parse it to correlate runs to PR commits and to confirm the run belongs to this PR. For each matched run, fetch the commit's changed files and CI check-run results from `<REPO>`. Also fetch the PR's review comments/findings (`--paginate`) so finding content can be compared across review cycles."
Relevance

●●● Strong

Placeholder and context-variable mismatches are concrete correctness issues; similar
context-contract feedback was at least partially accepted.

PR-#172

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The skill explicitly defines and uses $DISPATCH_REPO/$REPO_FULL_NAME in its run-finding
commands, but the newly added collector prompt uses angle-bracket placeholders for repos, which are
not otherwise defined in the skill and may be treated literally by subagents.

skills/retro-analysis/SKILL.md[15-20]
skills/retro-analysis/SKILL.md[26-36]
skills/retro-analysis/SKILL.md[124-137]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The flapping data collector prompt uses `<DISPATCH_REPO>` and `<REPO>` placeholders that are not established elsewhere in the skill, despite the skill defining `$DISPATCH_REPO` and consistently using `$REPO_FULL_NAME` for repo-qualified `gh` calls.

### Issue Context
Within `skills/retro-analysis/SKILL.md`, the Setup section defines `DISPATCH_REPO` and subsequent commands use `$DISPATCH_REPO` / `$REPO_FULL_NAME`. The newly added flapping collector prompt should follow the same convention (or explicitly define how placeholders are substituted).

### Fix Focus Areas
- skills/retro-analysis/SKILL.md[124-136]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Assumed event_payload log line 🐞 Bug ☼ Reliability
Description
The flapping data collector prompt unconditionally claims each dispatch-repo run log contains an
event_payload JSON line with pull_request.head.sha and pull_request.number. Repo documentation
for locating dispatch runs currently relies on timestamp/headBranch correlation and does not
establish this log line as a guaranteed interface, so the new guidance can break flapping detection
if the log format differs or the line is absent.
Code

skills/retro-analysis/SKILL.md[136]

+- **Flapping data collector:** "Find all code, fix, and review workflow runs related to PR #<PR_NUMBER> in `<DISPATCH_REPO>`. Each run's log contains an `event_payload` JSON line with `pull_request.head.sha` and `pull_request.number`; parse it to correlate runs to PR commits and to confirm the run belongs to this PR. For each matched run, fetch the commit's changed files and CI check-run results from `<REPO>`. Also fetch the PR's review comments/findings (`--paginate`) so finding content can be compared across review cycles."
Relevance

●● Moderate

Reliability concern is plausible, but the PR explicitly relies on event_payload; available precedent
only partially supports hardening.

PR-#172

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new text asserts a specific log-emitted event_payload line, but the repository’s run-finding
skill uses timestamp/headBranch correlation and does not reference any such log line. Additionally,
pre-scripts only reference event_payload in comments about validating dispatch inputs, not as an
emitted log record that can be reliably parsed later.

skills/retro-analysis/SKILL.md[124-137]
skills/finding-agent-runs/SKILL.md[55-65]
skills/finding-agent-runs/SKILL.md[74-89]
scripts/pre-code.sh[2-6]
scripts/pre-fix.sh[2-7]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new flapping collector instructions depend on a specific `event_payload` JSON log line for correlating runs to PR commits. This is not documented elsewhere as a guaranteed log output, and existing run-finding guidance uses timestamp/headBranch matching.

### Issue Context
`skills/finding-agent-runs/SKILL.md` demonstrates the established approach: find shim runs in the source repo and match dispatch-repo runs by timestamp/headBranch. `scripts/pre-code.sh` and `scripts/pre-fix.sh` mention `event_payload` only as an input-validation concern, not as a logged artifact.

### Fix Focus Areas
- skills/retro-analysis/SKILL.md[124-137]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread skills/retro-analysis/SKILL.md Outdated
Comment on lines +124 to +126
## Flapping detection

Check whether the workflow exhibits fix-break oscillation. Flapping wastes agent cycles and often indicates a deeper problem (conflicting instructions, flaky tests, or an approach the agent cannot converge on).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Protected skills/ file modified 📜 Skill insight § Compliance

This PR modifies skills/retro-analysis/SKILL.md, which is a protected governance/infrastructure
path requiring explicit human review and must not be auto-approved. Ensure appropriate
reviewers/CODEOWNERS sign off before merge.
Agent Prompt
## Issue description
The PR changes a protected path (`skills/`), which must not be auto-approved and requires explicit human review.

## Issue Context
Protected governance/infrastructure paths require elevated scrutiny. This PR updates the retro-analysis skill content under `skills/`.

## Fix Focus Areas
- skills/retro-analysis/SKILL.md[124-126]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread skills/retro-analysis/SKILL.md Outdated
Comment thread skills/retro-analysis/SKILL.md Outdated

Dispatch a subagent to identify code/fix/review workflow runs for the PR and collect the data needed for pattern detection:

- **Flapping data collector:** "Find all code, fix, and review workflow runs related to PR #<PR_NUMBER> in `<DISPATCH_REPO>`. Each run's log contains an `event_payload` JSON line with `pull_request.head.sha` and `pull_request.number`; parse it to correlate runs to PR commits and to confirm the run belongs to this PR. For each matched run, fetch the commit's changed files and CI check-run results from `<REPO>`. Also fetch the PR's review comments/findings (`--paginate`) so finding content can be compared across review cycles."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Assumed event_payload log line 🐞 Bug ☼ Reliability

The flapping data collector prompt unconditionally claims each dispatch-repo run log contains an
event_payload JSON line with pull_request.head.sha and pull_request.number. Repo documentation
for locating dispatch runs currently relies on timestamp/headBranch correlation and does not
establish this log line as a guaranteed interface, so the new guidance can break flapping detection
if the log format differs or the line is absent.
Agent Prompt
### Issue description
The new flapping collector instructions depend on a specific `event_payload` JSON log line for correlating runs to PR commits. This is not documented elsewhere as a guaranteed log output, and existing run-finding guidance uses timestamp/headBranch matching.

### Issue Context
`skills/finding-agent-runs/SKILL.md` demonstrates the established approach: find shim runs in the source repo and match dispatch-repo runs by timestamp/headBranch. `scripts/pre-code.sh` and `scripts/pre-fix.sh` mention `event_payload` only as an input-validation concern, not as a logged artifact.

### Fix Focus Areas
- skills/retro-analysis/SKILL.md[124-137]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review sweep: 5 findings on the flapping detection addition (1 critical, 4 medium).

Comment thread skills/retro-analysis/SKILL.md Outdated

### Patterns to detect

1. **File oscillation:** the same file was changed in two or more consecutive runs, and the changes reverse each other (lines added in run N were removed in run N+1, or vice versa).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Pattern 1 (file oscillation) fires on a single reversal, contradicting the skill's own "When NOT to flag" rule

Pattern 1's definition (line 140: "the same file was changed in two or more consecutive runs, and the changes reverse each other ... lines added in run N were removed in run N+1") triggers on a single N/N+1 reversal — exactly two runs. But the "When NOT to flag" section added in the same diff (lines 155-159) states "A single rework cycle (review requested changes, fix addressed them, review approved) is normal" and "Only flag when you see the same changes being applied and reversed repeatedly." These two sections of the same PR contradict each other: following Pattern 1 literally will flag the default happy-path review-fix cycle (code adds X, review flags X, fix removes X) as flapping, which is precisely the false-positive the exclusion section exists to prevent.

This is compounded by "consecutive runs" being ambiguous given the real workflow sequence is code → review → fix → review → fix, where review runs typically touch no files — if "consecutive" means consecutive workflow runs, oscillation almost never fires; if it means consecutive file-changing runs, it collapses back into the single-reversal false positive. It's also the only one of the three patterns with a 2-run threshold: Pattern 2 requires pass-fail-pass (3 runs) and Pattern 3 requires "more than 2" cycles (3+), so Pattern 1 is inconsistent with its siblings as well as with the exclusion list.

Suggestion: align Pattern 1 with the "repeated reversals" bar used everywhere else in the section — require at least two reversals (e.g. A→B→A across three file-changing runs) rather than a single undo, and explicitly define "consecutive" as consecutive file-changing (code/fix) runs, with review runs used only to correlate finding text rather than for the file-diff comparison.

Comment thread skills/retro-analysis/SKILL.md Outdated

### Applicability

Flapping detection applies to PR-based workflows with code/fix cycles. If `$ORIGINATING_URL` is an issue URL, check whether a PR is linked (`gh issue view "$ORIGINATING_URL" --json closedByPullRequestsReferences`) before skipping. If no linked PR exists, skip flapping detection for this retro.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: <PR_NUMBER> placeholder used in the data-gathering prompt is never derived

The Applicability section (this line) only covers the case where $ORIGINATING_URL is an issue URL ("check whether a PR is linked ... before skipping"); it says nothing about the common case where $ORIGINATING_URL is already a PR URL, and in neither branch does it bind a value to the <PR_NUMBER> placeholder used two lines later in the data-gathering prompt ("Find all code, fix, and review workflow runs related to PR #<PR_NUMBER>", line 136). No earlier section in SKILL.md defines $ORIGINATING_URL, PR_NUMBER, or any variable a subagent could substitute here (the Setup section only defines $REPO_FULL_NAME/$DISPATCH_REPO). A subagent following this literally has no stated source for <PR_NUMBER> in either branch.

Suggestion: add one line per branch — if $ORIGINATING_URL is a PR URL, extract its number directly; if it's an issue URL, use the linked PR's number from closedByPullRequestsReferences — and reference that resolved value explicitly when introducing the data-gathering prompt.

Comment thread skills/retro-analysis/SKILL.md Outdated

Dispatch a subagent to identify code/fix/review workflow runs for the PR and collect the data needed for pattern detection:

- **Flapping data collector:** "Find all code, fix, and review workflow runs related to PR #<PR_NUMBER> in `<DISPATCH_REPO>`. Each run's log contains an `event_payload` JSON line with `pull_request.head.sha` and `pull_request.number`; parse it to correlate runs to PR commits and to confirm the run belongs to this PR. For each matched run, fetch the commit's changed files and CI check-run results from `<REPO>`. Also fetch the PR's review comments/findings (`--paginate`) so finding content can be compared across review cycles."

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: <REPO> in the data-gathering prompt is not bound to the linked PR's repo in the cross-repo issue case

This PR's own linked issue demonstrates the gap: the issue lives in one repo and the linked PR (this one) lives in a different repo (closedByPullRequestsReferences on the issue resolves to a PR in a different repo than the issue itself). The Applicability check resolves a linked PR from an issue URL but never states which repo <REPO> (used here: "fetch the commit's changed files and CI check-run results from <REPO>") should resolve to when the linked PR lives in a different repo than $REPO_FULL_NAME.

Note this is distinct from the existing bot review comment on this line (which flags that <DISPATCH_REPO>/<REPO> are literal unsubstituted tokens vs. the skill's $DISPATCH_REPO/$REPO_FULL_NAME convention) — even if that comment's suggested fix of substituting $REPO_FULL_NAME is applied, it would resolve to the wrong repo in exactly this cross-repo scenario, since the actual PR/CI data lives in the linked PR's own repo, not the issue's repo.

Suggestion: explicitly bind <REPO> to the repository field returned by the linked PR (from closedByPullRequestsReferences) when flapping detection is entered via the issue-URL branch, noting it may differ from $REPO_FULL_NAME.

Comment thread skills/retro-analysis/SKILL.md Outdated
### Patterns to detect

1. **File oscillation:** the same file was changed in two or more consecutive runs, and the changes reverse each other (lines added in run N were removed in run N+1, or vice versa).
2. **Test result flipping:** a test that passed after run N fails after run N+1, then passes again after run N+2, and the flapping test covers a file the agent modified in the same run. Tests that flip independently of agent changes may be pre-existing flaky tests, not agent-caused oscillation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: Pattern 2 (test result flipping) needs per-test/file-coverage data the collector never fetches

Pattern 2 requires identifying "a test that passed after run N fails after run N+1, then passes again after run N+2, and the flapping test covers a file the agent modified in the same run" — i.e., both a specific failing test and a test-to-file coverage mapping. The only CI data the collector prompt (line 136) fetches is "CI check-run results," which GitHub's Checks API returns at job/suite granularity (e.g. a single "unit-tests" check), not per-test, and carries no file-coverage mapping. No heuristic is given for how a subagent would derive individual test identity or test-to-file coverage from check-run-level data alone.

Suggestion: either specify a concrete heuristic (e.g. parse per-test names out of CI log output/test-report artifacts, if available) or relax Pattern 2 to what check-run-level data actually supports (e.g. "the same named CI job flips status across 3+ runs while covering the same changed files").

Comment thread skills/retro-analysis/SKILL.md Outdated

1. **File oscillation:** the same file was changed in two or more consecutive runs, and the changes reverse each other (lines added in run N were removed in run N+1, or vice versa).
2. **Test result flipping:** a test that passed after run N fails after run N+1, then passes again after run N+2, and the flapping test covers a file the agent modified in the same run. Tests that flip independently of agent changes may be pre-existing flaky tests, not agent-caused oscillation.
3. **Cycle count:** more than 2 review-fix cycles on the same PR without convergence (the review keeps raising the same or alternating findings, e.g. a fix for one issue reintroducing a previously resolved one). A single rework cycle where the fix addresses the feedback and the review approves is normal iteration, not flapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: Hardcoded "more than 2" cycle threshold presents an explicitly unresolved design question as settled

Pattern 3 states as fact that "more than 2 review-fix cycles on the same PR without convergence" is flapping. The design doc this feature implements (docs/problems/flapping-convergence.md in fullsend-ai/fullsend) explicitly says thresholds must vary per repo/task type in its "Thresholds and configuration" section ("A documentation repo might tolerate only 2 review cycles ... A complex backend service might allow 5 cycles ... Default thresholds should be conservative and configurable per repo and per agent role"), and its "Open questions" section lists "What is the right default flapping budget?" as unanswered. This PR hardcodes a fixed, non-configurable "2" in the skill without referencing the doc or noting it as a provisional default.

Suggestion: reference docs/problems/flapping-convergence.md and phrase the threshold as a starting heuristic pending configurable per-repo thresholds, rather than an evidence-based fixed number.

Teach the retro agent to detect fix-break oscillation during
post-workflow analysis, kept distinct from test flakiness: file
oscillation (repeated A->B->A reversals across file-changing runs),
check-status flipping (a named CI check flipping across runs whose
commits touch overlapping files), and non-converging review-fix cycle
count.

Resolves the target PR and its repo explicitly (PR_NUMBER, PR_REPO),
handling the cross-repo issue-to-PR case, stays forge-agnostic (linkage
via the forge-specific skill), and treats the cycle threshold as a
provisional heuristic per flapping-convergence.md rather than a fixed
number.

Supersedes fullsend-ai#540. Closes fullsend-ai/fullsend#5512

Signed-off-by: Benjamin Kapner <bkapner@redhat.com>
@Benkapner
Benkapner force-pushed the feat/retro-flapping-detection-v2 branch from f7b5838 to b1d7da8 Compare August 26, 2026 10:28
@Benkapner

Copy link
Copy Markdown
Author

Thanks for the detailed review. I rebased this onto latest main (it was well behind and conflicting) and rewrote the flapping section to fit the current skill structure — it now sits right after the new Test flakiness section and stays forge-agnostic (PR/issue linkage goes through the forge-specific skill rather than inline gh). All five findings are addressed:

  1. CRITICAL — Pattern 1 fired on a single reversal. Now requires a repeated A→B→A pattern across three or more consecutive file-changing runs, with "consecutive" defined as consecutive file-changing (code/fix) runs and review runs used only to correlate finding text. A single add-then-remove is explicitly the normal review-fix cycle, consistent with "When NOT to flag."
  2. <PR_NUMBER> never derived. Applicability now resolves PR_NUMBER per branch — directly when the retro originates from a PR, or from the linked PR when it originates from an issue — and all data gathering uses the resolved value.
  3. <REPO> not bound in the cross-repo case. Now binds PR_REPO to the linked PR's repo, notes it may differ from $REPO_FULL_NAME, and fetches CI/file data from PR_REPO.
  4. Pattern 2 needed per-test data we can't fetch. Relaxed to check-status flipping at the named-CI-check level (what the Checks API actually provides), across 3+ runs with overlapping changed files; flip-without-overlap cases are pointed at the Test flakiness section instead.
  5. Hardcoded "more than 2" threshold. Now phrased as a provisional starting heuristic that links to flapping-convergence.md, noting the default is an open question and should be configurable per repo/role.

The literal <DISPATCH_REPO>/<REPO> token issue from the earlier bot pass is covered by the same variable-binding change. Since the rebase replaced the diff, the earlier inline threads are stale — happy to reopen any if I missed the intent.

@Benkapner
Benkapner requested a review from waynesun09 August 26, 2026 10:29

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review-only pass on the new Flapping detection section. 9 inline findings (5 high, 4 medium), all on skills/retro-analysis/SKILL.md.

The five high findings cluster on one root cause: the run→PR correlation model in the data-gathering prompt does not match how dispatches actually carry their payload. Code runs have no pull_request object at all, and where one exists its head.sha is the pre-run head — so the first file-changing run is dropped and every remaining run is attributed its predecessor's diff. That shifts the run N / N+1 / N+2 numbering both Pattern 1 and Pattern 2 are built on. Separately, the collector never requests patch content, so Pattern 1's line-level A→B→A test has no data to run against, and Pattern 2 lacks the repeat bar Pattern 1 was given, so it fires on ordinary regress-and-fix.

The medium findings are narrower: linked-PR resolution assumes a single PR where the recipe returns a list, the linkage pointer names a skill that does not hold the recipe, run discovery is unbounded against --limit 10 recipes, and "order runs by commit" is undefined across the rebases the fix agent performs.

Not a blocking verdict — flagging for the author and code owners to triage.

- **Flapping data collector:** "Find all code, fix, and review workflow
runs related to PR #`PR_NUMBER` in `$DISPATCH_REPO`. Each run's log
carries an `event_payload` JSON line with `pull_request.head.sha` and
`pull_request.number`; parse it to correlate runs to PR commits and

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Code runs carry no pull_request in event_payload, so PR-number matching silently drops the first file-changing run

The collector prompt tells the subagent to parse pull_request.head.sha / pull_request.number and "confirm the run belongs to this PR". Code runs never carry a pull_request object.

Verified from primary sources in fullsend-ai/fullsend: ADR 0033 states the per-org dispatch.yml "builds a minimal payload from $GITHUB_EVENT_PATH (extracting only issue, pull_request, and comment fields)", and docs/contributing/ci-workflows.md gives the canonical reusable-code.yml concurrency key as fullsend-code-agent-${{ inputs.source_repo }}-${{ fromJSON(inputs.event_payload).issue.number || fromJSON(inputs.event_payload).pull_request.number }} — the platform's own code-agent key falls back to issue.number precisely because pull_request is absent for code dispatches. This repo's skills/finding-agent-runs/SKILL.md independently confirms the trigger: "Code dispatches from issue events when ready-to-code is applied."

The code run is the first file-changing run (it creates the branch and the PR), so under the rule as written it fails confirmation and is discarded, destroying the baseline for Pattern 1's run N / N+1 / N+2 numbering. Conversely, a comment-triggered fix run puts the PR number in issue.number, so issue.number is not always an issue number.

Suggestion: spell out a three-way match rule mirroring the platform's own concurrency key — a run belongs to this PR if pull_request.number == PR_NUMBER, OR issue.number == PR_NUMBER (comment-triggered fix), OR issue.number equals the issue extracted from the agent/{issue}-{slug} branch (code runs). That branch convention is already documented at line 30 of this same file and in skills/finding-agent-runs/SKILL.md.

runs related to PR #`PR_NUMBER` in `$DISPATCH_REPO`. Each run's log
carries an `event_payload` JSON line with `pull_request.head.sha` and
`pull_request.number`; parse it to correlate runs to PR commits and
confirm the run belongs to this PR. For each matched run, fetch the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: pull_request.head.sha is the pre-run head, so "the commit's changed files" attributes each run the previous run's diff

Lines 159-160 instruct: "For each matched run, fetch the commit's changed files ... from PR_REPO." But the payload is built at dispatch time from the triggering event — ADR 0033 (fullsend-ai/fullsend): the dispatch workflow "builds a minimal payload from $GITHUB_EVENT_PATH" — so head.sha is the branch head before the run does any work.

Every code/fix run is therefore attributed the diff its predecessor produced, shifting the entire sequence by one cycle. That directly corrupts the A→B→A reversal analysis in Pattern 1 (lines 170-174) that this whole section exists to support: the reversal will appear to occur one run earlier than it did, and the last run's actual output is never examined at all.

Useful corollary from the same dispatch model: a review run is triggered by a pull_request event on the pushed commit, so a review run's head.sha is the output commit of the preceding code/fix run — making review runs a reliable per-cycle anchor.

Suggestion: define run N's changes as the diff between head.sha(run N) and head.sha(next run) — equivalently, the head SHA of the review run that follows it — rather than the changed files of head.sha(run N). State this explicitly in the collector prompt, since the naive reading ("the run's commit") is wrong for every code/fix run.

`PR_NUMBER` and collect what pattern detection needs:

- **Flapping data collector:** "Find all code, fix, and review workflow
runs related to PR #`PR_NUMBER` in `$DISPATCH_REPO`. Each run's log

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Run/PR correlation ignores the source repo, but the dispatch repo is org-wide

$DISPATCH_REPO is ${ORG}/.fullsend (line 19) — a single repo serving every repository in the org. Issue/PR numbers are not unique across repos in an org, so matching on pull_request.number == PR_NUMBER alone can pull a numerically-coincident PR's runs from a completely different repo into the flapping analysis, producing fabricated oscillation.

Disambiguation is available and is already the platform's own practice: source_repo is a first-class dispatch input (internal/harnessdispatch/ref.go: SourceRepo string \json:"source_repo"`; ADR 0034 lists it among the workflow_callinputs), andreusable-code.yml's concurrency key keys on inputs.source_repo` alongside the number for exactly this reason.

Compounding this, DISPATCH_REPO is derived at lines 18-19 from $REPO_FULL_NAME's org, yet lines 144-145 explicitly permit PR_REPO to differ from $REPO_FULL_NAME — when the resolved PR lives under a different org, this line searches the wrong dispatch repo entirely and returns nothing, silently.

Suggestion: require both halves — (a) a run matches only if source_repo (or pull_request.base.repo.full_name) equals PR_REPO AND the number matches; and (b) re-derive DISPATCH_REPO from PR_REPO's org once PR_REPO is resolved, stated alongside the existing "use PR_NUMBER and PR_REPO (not $REPO_FULL_NAME)" instruction at line 147.

`pull_request.number`; parse it to correlate runs to PR commits and
confirm the run belongs to this PR. For each matched run, fetch the
commit's changed files and the named CI check results from `PR_REPO`.
Also fetch the PR's review comments/findings across all pages so

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Data collector cannot supply the line-level diffs Pattern 1 requires

Pattern 1 (lines 170-174) requires detecting that "the same lines in a file are changed, reverted, then changed again" — a repeated A→B→A on specific lines. That needs per-run patches/hunks.

The collector prompt (lines 155-162) only asks for "the commit's changed files" (a file-path list), "the named CI check results", and review comments. It never requests patch content, so an agent following the recipe literally can observe only that the same file path was touched three times. The skill's own "When NOT to flag" list states "Different files changing across runs is normal iteration", and the section repeatedly warns against path-level inference — so the agent will either skip Pattern 1 entirely (no data) or over-flag any file touched three times. Pattern 1 is the flagship pattern of the section and is not executable as specified.

Suggestion: update the collector instructions to fetch per-commit/per-range patches for file-changing runs (the commit or compare diff), and instruct the agent to compare hunks/line ranges across runs rather than file-path lists. This pairs with the head.sha finding above: the diff to fetch is between consecutive runs' head SHAs.

2. **Check-status flipping:** the same named CI check (e.g. `unit-tests`)
flips pass→fail→pass across three or more runs whose commits touch
overlapping changed files. CI results are reported at check/job level,
not per test, so compare at the named-check level. A check that flips

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH: Pattern 2 lacks the repeat bar Pattern 1 was given, so it fires on ordinary regress-and-fix

Pattern 2 (lines 175-180) flags whenever the same named CI check goes pass→fail→pass across three or more runs with overlapping changed files. That is exactly the shape of an ordinary single-mistake recovery: code lands green, one fix regresses a test, the next fix restores it. Since "overlapping changed files" is true for nearly every fix sequence on the same PR, this will fire routinely.

The asymmetry is textual and self-contradicting: Pattern 1 was deliberately tightened in this same PR to require a repeated A→B→A and explicitly excludes "a single add-then-remove" as "the normal review-fix cycle", and "When NOT to flag" says "A single rework cycle ... is normal" and "Only flag when the same change is applied and reversed repeatedly". Pattern 2 carries no equivalent safeguard.

The asymmetry is also substantive, not just cosmetic: for line diffs, A→B→A signals a contested change with no stable answer, whereas for CI checks pass is the desired attractor, so pass→fail→pass is convergence, not oscillation. As written, Pattern 2 will file "Flapping detected:" issues against healthy PRs — the exact noise the mandatory "Before proposing: check for existing issues" section exists to prevent.

Suggestion: tighten Pattern 2 to require a second flip (pass→fail→pass→fail, or two full oscillations) before flagging, or require the failure to reappear after an intervening pass on the same check with overlapping agent diffs — mirroring the stricter bar Pattern 1 uses. A single regress-then-recover should be listed under "When NOT to flag".

number and `PR_REPO` is its `owner/repo` (`$REPO_FULL_NAME`).
- If the retro originates from an issue, resolve the linked PR using your
forge-specific skill's issue-to-PR linkage recipe. If no linked PR
exists, skip flapping detection for this retro. Otherwise set

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: "Resolve the linked PR" assumes exactly one linked PR; the only available recipe returns a list of up to 50

The Applicability branch reads as a two-way choice — exactly one linked PR, or none ("If no linked PR exists, skip flapping detection ... Otherwise set PR_NUMBER and PR_REPO from the linked PR").

The actual recipe contradicts that shape: skills/github-forge/SKILL.md returns closedByPullRequestsReferences(first: 50) { nodes { number url author { login } state } } — a list of up to 50, each carrying state. This PR's own originating issue (fullsend-ai/fullsend#5512) resolves to multiple linked PRs across two repos, including the superseded attempt #540. With no tie-break, the retro agent may analyse an abandoned attempt's run history and report flapping for a PR nobody is working on. Note the same query returns no repository field, so PR_REPO can only be derived by parsing each node's url.

Suggestion: state the tie-break explicitly, using the state field the query already returns — prefer the single open linked PR; if none is open, take the most recently updated; if several are open, either skip flapping detection or analyse each separately and say which PR each finding refers to. Also say to derive PR_REPO by parsing the node's url.

- If the retro originates from a PR, use it directly: `PR_NUMBER` is its
number and `PR_REPO` is its `owner/repo` (`$REPO_FULL_NAME`).
- If the retro originates from an issue, resolve the linked PR using your
forge-specific skill's issue-to-PR linkage recipe. If no linked PR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: The issue-to-PR linkage reference points at a skill that does not contain the recipe

Lines 141-142 say to "resolve the linked PR using your forge-specific skill's issue-to-PR linkage recipe." Everywhere else in this file "your forge-specific skill" means the forge variant of this skill — see line 13 ("Use the forge-specific retro-analysis skill for CLI recipes"), and lines 43 and 213.

Verified at head: neither skills/retro-analysis/github/SKILL.md nor skills/retro-analysis/gitlab/SKILL.md contains any linkage recipe (the GitHub file covers workflow tracing, log reading, agents-repo discovery, and duplicate search only). The recipe actually lives in the separate shared forge skills — skills/github-forge/SKILL.md has the closedByPullRequestsReferences GraphQL query, and gitlab-forge is the GitLab counterpart. Both are loaded for the retro agent (harness/retro.yaml lists skills/github-forge and skills/gitlab-forge under forge.*.skills), so the capability is present — the pointer just sends the agent to a file that does not have it, and the agent will find nothing at the location the file's own convention names.

Suggestion: name the skill explicitly — "use the github-forge / gitlab-forge skill's linked-PR/MR recipe (closedByPullRequestsReferences / closed_by)" — rather than the ambiguous "your forge-specific skill", which this file uses to mean retro-analysis/{github,gitlab}.


### Data gathering

Dispatch a subagent to identify the code/fix/review workflow runs for

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: Run discovery is unbounded, and every existing recipe caps at 10 runs

"Find all code, fix, and review workflow runs related to PR #PR_NUMBER in $DISPATCH_REPO" has no bound and no server-side filter available: dispatch-repo runs are workflow_dispatch with no PR in their ref/branch, so per this section's own premise the only way to identify a run is to download its full log and parse event_payload.

Verified at head, every run-listing recipe the retro agent has uses --limit 10 (skills/retro-analysis/github/SKILL.md: --workflow=code.yml --limit 10, review.yml --limit 10, fix.yml --limit 10; the GitLab file uses per_page=20). In a busy org dispatch repo those windows cover on the order of an hour or two. A PR that flaps for days is exactly the case where the target runs are furthest back, so as written the collector will usually find none of them — or will download hundreds of run logs against the retro harness's 30-minute timeout_minutes (harness/retro.yaml) before synthesis begins.

Suggestion: bound the search explicitly — derive a time window from the PR's createdAt..updatedAt and pass --created <start>..<end> with a raised --limit per workflow, and note that each candidate costs a full log fetch so the window must be bounded before iterating. Reuse the finding-agent-runs discovery recipe rather than reinventing run discovery here.


### Patterns to detect

Order runs by commit and count only **file-changing** (code/fix) runs as

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEDIUM: "Order runs by commit" is undefined across force-pushes and does not de-duplicate same-SHA reruns

This line says "Order runs by commit and count only file-changing (code/fix) runs as steps". Commit order is only a partial order in practice.

The fix agent rebases and amends, so a head.sha recorded in an earlier run's payload is frequently no longer reachable from the PR head — the rewritten commits are new objects with no ancestry relation to the recorded SHAs, leaving "order by commit" undefined for exactly the long-lived PRs this section targets. Worse, a rebase makes a file look "reverted then re-applied" when nothing oscillated — a direct false positive for Pattern 1.

Separately, workflow reruns on the same SHA produce duplicate runs with no file change, shifting the N / N+1 / N+2 step indexing that both Pattern 1 and Pattern 2 depend on.

Suggestion: order runs by workflow run creation time rather than by commit; collapse same-SHA reruns into a single step; and add force-push/rebase to "When NOT to flag" — if a recorded head.sha is no longer reachable from the PR head, treat the reversal signal as unreliable rather than as oscillation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(retro): detect flapping and fix-break oscillation in retro analysis

2 participants